package com.fdb.smcts.redis.lock;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisCluster;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @author xiaojunhui
 */
@Aspect
@Component
public class LockMethodAspect {

    @Autowired
    private RedisLockHelper redisLockHelper;
    @Autowired
    private JedisCluster jedisCluster;
    private Logger logger = LoggerFactory.getLogger(LockMethodAspect.class);

    @Around("@annotation(com.fdb.smcts.redis.lock.RedisLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("获取锁开始");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获取函数的参数名
        String[] parameterNames = signature.getParameterNames();
        //获取redislock注解对象
        RedisLock redisLock = method.getAnnotation(RedisLock.class);
        String lockValue = UUID.randomUUID().toString();
        //获取注解配置的用于生成key的参数名
        String[] keyParamNameArr = redisLock.keyParamNames();
        //最终生成的锁key
        StringBuilder lockKey = new StringBuilder();
        lockKey.append(redisLock.keyPreFarmat());
        //锁优先使用参数，如果配置的参数为空则使用指定的key
        if (keyParamNameArr.length > 0) {
            Object[] args = joinPoint.getArgs();
            List<String> keyParamNameList = Arrays.asList(keyParamNameArr);
            for (int i = 0; i < parameterNames.length; i++) {
                if (keyParamNameList.contains(parameterNames[i])) {
                    lockKey.append(String.valueOf(args[i]));
                }
            }
        } else {
            lockKey.append(redisLock.key());
        }
        logger.info("设置分布式锁：" + lockKey.toString());
        try {
            boolean islock = false;
            //获取当前时间
            while (!islock) {
                islock = redisLockHelper.lock(jedisCluster, lockKey.toString(), lockValue, redisLock.expire(), redisLock.timeUnit());
                if (!islock) {
                    Thread.sleep(300);
                }
            }
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            logger.error("系统异常:" + throwable);
            throw throwable;
        } finally {
            logger.info("释放锁");
            redisLockHelper.unlock(jedisCluster, lockKey.toString(), lockValue);
        }
    }
}

